package serverManager;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.mortbay.util.ajax.Continuation;

import os_listener.FileBytes;
import os_listener.ServerUpdates;
import db_manager.FileManager;
import db_manager.Manager;

/**
 * this class waits for users to create or delete files from their dropboxes and then
 * reacts accordingly
 */
public class UploadFile extends Thread
{
	ServerSocket serverSocket=null;
	Socket clientSocket=null;
	boolean serverIsUp;
	Manager manager;
	
	/**
	 * the constructor of this class
	 * @throws IOException
	 */
	public UploadFile() throws IOException
	{
		manager = Manager.getInstance();
		serverIsUp = true;
		serverSocket = new ServerSocket(5555);
	}

	/**
	 * the Thread execution function
	 */
	public void run()
	{
		File sessionFile = new File("C:\\Program Files\\m&l_dropbox_server\\hibernate.cfg.xml");
		// Opening a new session factory - for connection with DB
		SessionFactory sessionFactory = new Configuration().configure(sessionFile).buildSessionFactory();
		try
		{
			// as long as the server program is up keep running
			while(serverIsUp)
			{
				// waits for an update
				clientSocket=serverSocket.accept();
				//creating session for connecting to DB
				Session session = sessionFactory.openSession();

				ObjectInputStream in  = new ObjectInputStream(clientSocket.getInputStream());
				PrintWriter out = new PrintWriter(clientSocket.getOutputStream(),true);
				// getting the fileBytes - update
				FileBytes fb = (FileBytes) in.readObject();

				String dirName, hash, fileName;
				fileName = fb.getFileName();
				dirName = fb.getPath().substring(0, fb.getPath().lastIndexOf('\\'));
				dirName = dirName.substring(dirName.lastIndexOf('\\')+1);
				long dir_id = manager.findDirId(session, dirName, fb.getUserName());
				// the case that a new file was created
				if(fb.isCreated())
				{
					FileManager fm = new FileManager(session);
					/*
					 * the case that the file needs to be checked if it already
					 * exists in the server
					 */
					if(fb.getBytes() == null)
					{
						boolean does_exist = manager.wasLoaded(session, fb.getHash());
						db_manager.File file = new db_manager.File();
						file.setFile_name(fileName);
						file.setDir_id(dir_id);
						hash = fb.getHash();
						file.setHash(hash);
						// updating the DB
						fm.saveFile(file);
						session.flush();
						// the case that the file already exists in the DB
						if(does_exist)
						{
							out.println("exist");
						}
						// the case that the file doesn't exist in the DB
						else
						{
							out.println("not exist");
							ObjectInputStream in2  = new ObjectInputStream(clientSocket.getInputStream());
							FileBytes fb2 = (FileBytes) in2.readObject();
							//creating session for connecting to DB
							Session session2 = sessionFactory.openSession();
							long file_id = manager.getfile_id(session2, file);
							// the case that the file was accepted
							if(fb2.getBytes() != null)
							{
								// create the file
								convByteToFile(fb2.getBytes(), "c:/uploadedServerFiles" + "/" + file_id);
							}
							session2.flush();
						}
						//creating session for connecting to DB
						Session session3 = sessionFactory.openSession();
						boolean isShared = manager.checkShared(session3, dirName, fb.getUserName());
						// the case that the file is in a shared dir
						if(isShared)
						{
							// getting the users that share the dir
							ArrayList<Long> users_id = manager.getSharedUsers(session3, dirName, fb.getUserName());
							ServerUpdates new_su = new ServerUpdates();
							// updating the instance of serverUpdates
							new_su.setHash(hash);
							new_su.setFile_name(fileName);
							new_su.setDir_name(dirName);
							new_su.setUpdate("new file in shared folder");
							ConnectionsKeeper ck = ConnectionsKeeper.getInstance();
							ck.setNewest_su(new_su);
							Continuation cc;
							// for each user that share this dir
							for (int i = 0; i < users_id.size(); i++)
							{
								// get the continuation
								cc = ck.getConnection(users_id.get(i));
								// the case that the user is connected to the spy program
								if(cc != null)
								{
									cc.resume();
								}
							}
						}
						session3.flush();
					}
				}
				// the case that a file was deleted
				else
				{
					manager.deleteFile(dir_id, fb.getFileName());
					session.flush();
					//creating session for connecting to DB
					Session session2 = sessionFactory.openSession();
					boolean isShared = manager.checkShared(session2, dirName, fb.getUserName());
					// the case that the file was in a shared dir
					if(isShared)
					{
						// getting the users that share the dir
						ArrayList<Long> users_id = manager.getSharedUsers(session2, dirName, fb.getUserName());
						ServerUpdates new_su = new ServerUpdates();
						// updating the instance of serverUpdates
						new_su.setFile_name(fileName);
						new_su.setDir_name(dirName);
						new_su.setUpdate("delete file from shared folder");
						ConnectionsKeeper ck = ConnectionsKeeper.getInstance();
						ck.setNewest_su(new_su);
						Continuation cc;
						// for each user that share the dir
						for (int i = 0; i < users_id.size(); i++)
						{
							// get the continuation
							cc = ck.getConnection(users_id.get(i));
							// the case that the user is connected to the spy program
							if(cc != null)
							{
								cc.resume();
							}
						}
					}
					session2.flush();
				}
			}
		}
		catch(Exception e) {}
	}

	/**
	 * ending this Thread
	 */
	public void shutDownServer()
	{
		try
		{
			// the case that this socket is open
			if (clientSocket != null)
			{
				clientSocket.close();
			}
			// the case that this server socket is open
			if (serverSocket != null) 
			{
				serverSocket.close();
			}
		}
		catch(Exception e) {}
		serverIsUp = false;
	}
	
	/*
	 * convert the bytes array into a file - taken from the web 
	 */
	private File convByteToFile(byte[] bytes, String path)
	{ 
		File newFile = new File(path);
		FileOutputStream fos = null;

		try 
		{
			fos = new FileOutputStream(newFile);
			fos.write(bytes);
			fos.flush();
			fos.close();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}      

		return newFile;
	}
}
